package br.com.linkcom.sined.modulo.sistema.controller.process;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.web.servlet.ModelAndView;

import br.com.linkcom.neo.authorization.process.ProcessAuthorizationModule;
import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MessageType;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;
import br.com.linkcom.neo.types.ListSet;
import br.com.linkcom.sined.geral.bean.Filme;
import br.com.linkcom.sined.geral.bean.Usuario;
import br.com.linkcom.sined.geral.bean.Usuarioator;
import br.com.linkcom.sined.geral.bean.Usuariofilme;
import br.com.linkcom.sined.geral.bean.Usuariogenero;
import br.com.linkcom.sined.geral.service.FilmeService;
import br.com.linkcom.sined.geral.service.UsuarioService;
import br.com.linkcom.sined.geral.service.UsuarioatorService;
import br.com.linkcom.sined.geral.service.UsuariofilmeService;
import br.com.linkcom.sined.geral.service.UsuariogeneroService;
import br.com.linkcom.sined.geral.service.UsuariopapelService;
import br.com.linkcom.sined.modulo.sistema.controller.process.bean.RecomendarFilmeBean;
import br.com.linkcom.sined.modulo.sistema.controller.process.filter.RecomendarFilmeFiltro;
import br.com.linkcom.sined.util.SinedUtil;

@Controller(path={"/sistema/process/Recomendarfilme"}, authorizationModule=ProcessAuthorizationModule.class)
public class RecomendarFilmeProcess extends MultiActionController {
	
	private UsuarioService usuarioService;
	private UsuariopapelService usuariopapelService;
	private UsuariofilmeService usuariofilmeService;
	private UsuarioatorService usuarioatorService;
	private UsuariogeneroService usuariogeneroService;
	private FilmeService filmeService;
	
	public void setUsuarioService(UsuarioService usuarioService) {
		this.usuarioService = usuarioService;
	}
	public void setUsuariopapelService(UsuariopapelService usuariopapelService) {
		this.usuariopapelService = usuariopapelService;
	}
	public void setUsuariofilmeService(UsuariofilmeService usuariofilmeService) {
		this.usuariofilmeService = usuariofilmeService;
	}
	public void setUsuarioatorService(UsuarioatorService usuarioatorService) {
		this.usuarioatorService = usuarioatorService;
	}
	public void setUsuariogeneroService(UsuariogeneroService usuariogeneroService) {
		this.usuariogeneroService = usuariogeneroService;
	}
	public void setFilmeService(FilmeService filmeService) {
		this.filmeService = filmeService;
	}
	
	@DefaultAction
	public ModelAndView index(WebRequestContext request, RecomendarFilmeFiltro filtro) throws Exception {
		
		List<Usuario> listaUsuario = new ArrayList<Usuario>();
		
		if (usuariopapelService.isAdmin()){
			listaUsuario.addAll(usuarioService.findAll());
		}else {
			listaUsuario.add(SinedUtil.getUsuarioLogado());
		}
		
		request.setAttribute("listaUsuario", listaUsuario);
		
		return new ModelAndView("process/recomendarFilme", "filtro", filtro);
	}
	
	public ModelAndView recomendar(WebRequestContext request, RecomendarFilmeFiltro filtro) throws Exception {
		
		Map<Integer, List<Usuariofilme>> mapaCdusuarioListaUsuariofilme = new HashMap<Integer, List<Usuariofilme>>();
		Map<Integer, Integer> mapaCdusuarioTotalPreferencias = getMapaCdusuarioTotalPreferencias(filtro);
		Map<Integer, List<Integer>> mapaTotalPreferenciasListaCdusuario = getMapaTotalPreferenciasListaCdusuario(mapaCdusuarioTotalPreferencias);
		List<Integer> listaTotal = new ArrayList<Integer>(mapaCdusuarioTotalPreferencias.values());
		List<Integer> listaCdusuario;
		List<Usuariofilme> listaUsuariofilme;
		List<Usuariofilme> listaUsuariofilmeAux;
		List<Integer> listaCdfilme = new ArrayList<Integer>();
		List<RecomendarFilmeBean> listaBean = new ArrayList<RecomendarFilmeBean>();
		List<Filme> listaFilme;
		Filme filme;
		RecomendarFilmeBean bean;
		
		Collections.sort(listaTotal, 
				new Comparator<Integer>(){
					public int compare(Integer o1, Integer o2) {
						return o2.compareTo(o1);						
					}								
				}
		);
		
		for (Integer cdusuario: mapaCdusuarioTotalPreferencias.keySet()){
			mapaCdusuarioListaUsuariofilme.put(cdusuario, usuariofilmeService.findForRecomendarFilme(filtro, new Usuario(cdusuario)));
		}
		
		for (Integer total: listaTotal){
			
			listaUsuariofilme = new ArrayList<Usuariofilme>();
			listaCdusuario = mapaTotalPreferenciasListaCdusuario.get(total);
			
			if (listaCdusuario!=null){
				for (Integer cdusuario: listaCdusuario){
					listaUsuariofilmeAux = mapaCdusuarioListaUsuariofilme.get(cdusuario);
					if (listaUsuariofilmeAux!=null && !listaUsuariofilmeAux.isEmpty()){
						listaUsuariofilme.addAll(listaUsuariofilmeAux);
					}
				}
			}
			
			Collections.sort(listaUsuariofilme, 
					new Comparator<Usuariofilme>(){
						public int compare(Usuariofilme o1, Usuariofilme o2) {
							return o2.getNota().compareTo(o1.getNota());						
						}								
					}
			);
			
			for (Usuariofilme usuariofilme: listaUsuariofilme){
				filme = usuariofilme.getFilme();
				System.out.println(filme.getTitulo());
				if (!listaCdfilme.contains(filme.getCdfilme())){
					bean = new RecomendarFilmeBean();
					bean.setFilme(filme);
					listaBean.add(bean);
					listaCdfilme.add(filme.getCdfilme());
				}
			}
		}		
		
		if (listaBean.isEmpty()){
			listaFilme = filmeService.findForRecomendarFilme(filtro.getUsuario());
			for (Filme filmeLoop: listaFilme){
				if (!listaCdfilme.contains(filmeLoop.getCdfilme())){
					bean = new RecomendarFilmeBean();
					bean.setFilme(filmeLoop);
					listaBean.add(bean);
					listaCdfilme.add(filmeLoop.getCdfilme());
				}
			}
		}
		
		if (listaBean.isEmpty()){
			listaUsuariofilme = usuariofilmeService.findForRecomendarFilmeMaiorNota();
			for (Usuariofilme usuariofilme: listaUsuariofilme){
				filme = usuariofilme.getFilme();
				if (!listaCdfilme.contains(filme.getCdfilme())){
					bean = new RecomendarFilmeBean();
					bean.setFilme(filme);
					listaBean.add(bean);
					listaCdfilme.add(filme.getCdfilme());
				}
			}
		}
		
		if (listaBean.isEmpty()){
			request.addMessage("Nenhum filme encontrado para recomendar", MessageType.ERROR);
		}
		
		filtro.setListaBean(listaBean);
		
		return new ModelAndView("process/recomendarFilme", "filtro", filtro);
	}
	
	private Map<Integer, Integer> getMapaCdusuarioTotalPreferencias(RecomendarFilmeFiltro filtro){
		
		Map<Integer, Integer> mapaCdusuarioTotalPreferencias = new HashMap<Integer, Integer>();
		Usuario usuario = usuarioService.loadForRecomendarFilme(filtro.getUsuario());
		
		Set<Usuarioator> listaUsuarioator1 = usuario.getListaUsuarioator();
		Set<Usuarioator> listaUsuarioator2 = new ListSet<Usuarioator>(Usuarioator.class, usuarioatorService.findForRecomendarFilme(filtro));
		Set<Usuariogenero> listaUsuariogenero1 = usuario.getListaUsuariogenero();
		Set<Usuariogenero> listaUsuariogenero2 = new ListSet<Usuariogenero>(Usuariogenero.class, usuariogeneroService.findForRecomendarFilme(filtro));
		
		Integer cdusuario;
		Integer total;
		
		if (listaUsuarioator1!=null && listaUsuarioator2!=null){
			for (Usuarioator usuarioator1: listaUsuarioator1){
				for (Usuarioator usuarioator2: listaUsuarioator2){
					if (usuarioator1.getAtor().getCdator().equals(usuarioator2.getAtor().getCdator())){
						cdusuario = usuarioator2.getUsuario().getCdusuario();
						total = mapaCdusuarioTotalPreferencias.get(cdusuario);
						if (total==null){
							total = 0;
						}
						total++;
						mapaCdusuarioTotalPreferencias.put(cdusuario, total);
					}
				}
			}
		}
		
		if (listaUsuariogenero1!=null && listaUsuariogenero2!=null){
			for (Usuariogenero usuariogenero1: listaUsuariogenero1){
				for (Usuariogenero usuariogenero2: listaUsuariogenero2){
					if (usuariogenero1.getGenero().equals(usuariogenero2.getGenero())){
						cdusuario = usuariogenero2.getUsuario().getCdusuario();
						total = mapaCdusuarioTotalPreferencias.get(cdusuario);
						if (total==null){
							total = 0;
						}
						total++;
						mapaCdusuarioTotalPreferencias.put(cdusuario, total);
					}
				}
			}
		}
		
		return mapaCdusuarioTotalPreferencias;
	}
	
	private Map<Integer, List<Integer>> getMapaTotalPreferenciasListaCdusuario(Map<Integer, Integer> mapaCdusuarioTotalPreferencias){
		
		Map<Integer, List<Integer>> mapaTotalPreferenciasListaCdusuario = new HashMap<Integer, List<Integer>>();
		Integer totalPreferencias;
		List<Integer> listaCdusuario;
		
		for (Integer cdusuario: mapaCdusuarioTotalPreferencias.keySet()){
			totalPreferencias = mapaCdusuarioTotalPreferencias.get(cdusuario);
			listaCdusuario = mapaTotalPreferenciasListaCdusuario.get(totalPreferencias);
			if (listaCdusuario==null){
				listaCdusuario = new ArrayList<Integer>();
			}
			listaCdusuario.add(cdusuario);
			mapaTotalPreferenciasListaCdusuario.put(totalPreferencias, listaCdusuario);
		}
		
		return mapaTotalPreferenciasListaCdusuario;
	}
}